package play.modules.ppdruid;

import com.alibaba.druid.filter.stat.StatFilterContext;
import com.alibaba.druid.filter.stat.StatFilterContextListenerAdapter;
import com.alibaba.druid.support.http.stat.*;
import com.alibaba.druid.support.profile.ProfileEntryKey;
import com.alibaba.druid.support.profile.ProfileEntryReqStat;
import com.alibaba.druid.support.profile.Profiler;
import play.Logger;
import play.Play;
import play.PlayPlugin;
import play.mvc.Http;
import play.mvc.Http.Request;
import play.mvc.Scope;

import java.util.Map;

/**
 * Created by eric on 16/5/24.
 */
public class WebStatPlugin extends PlayPlugin {
    protected WebStatFilterContextListener statFilterContextListener      = new WebStatFilterContextListener();
    protected WebAppStat webAppStat;
    protected final Integer sessionStatMaxCount = Integer.parseInt(Play.configuration.getProperty("sessionstat.maxCount", "1000"));
    protected final Boolean isSessionStatEnabled = Boolean.parseBoolean(Play.configuration.getProperty("sessionstat.enabled", "true"));
    protected final Boolean isProfileEnabled = Boolean.parseBoolean(Play.configuration.getProperty("webstat.profile.enabled", "false"));

    public String getSessionId() {
    	if(Request.current().params._contains("sessionId")) {
    		return Request.current().params.get("sessionId");
    	}
    	if(Request.current().headers.containsKey("sessionId")) {
    		return Request.current().headers.get("sessionId").value();
    	}
    	if(Request.current().cookies.containsKey("sessionId")) {
    		return Request.current().cookies.get("sessionId").value;
    	}
    	return Scope.Session.current().getId();
    }
    
    public WebSessionStat getSessionStat(Http.Request request) {
        if (!isSessionStatEnabled) {
            return null;
        }

        WebSessionStat sessionStat = null;
        String sessionId = getSessionId();
        if (sessionId != null) {
            sessionStat = webAppStat.getSessionStat(sessionId, true);
        }

        if (sessionStat != null) {
            long currentMillis = System.currentTimeMillis();

            String userAgent = request.headers.get("user-agent").value();

            if (sessionStat.getCreateTimeMillis() == -1L) {

                sessionStat.setCreateTimeMillis(currentMillis);


                webAppStat.computeUserAgent(userAgent);
                webAppStat.incrementSessionCount();
            }

            sessionStat.setUserAgent(userAgent);

            String ip = request.remoteAddress;

            sessionStat.addRemoteAddress(ip);
        }

        return sessionStat;
    }

    public String getPrincipal(Http.Request request) {
        return Scope.Session.current().get("connected");
    }


    public void onApplicationStart() {
        StatFilterContext.getInstance().addContextListener(statFilterContextListener);
        if (webAppStat == null) {
            webAppStat = new WebAppStat("/", sessionStatMaxCount);
        }
        WebAppStatManager.getInstance().addWebAppStatSet(webAppStat);

        Logger.info("WebStat plugin started...");
    }

    public void onApplicationStop() {
        StatFilterContext.getInstance().removeContextListener(statFilterContextListener);

        if (webAppStat != null) {
            WebAppStatManager.getInstance().remove(webAppStat);
        }
    }

    public void beforeInvocation() {
        Http.Request request = Http.Request.current();
        long startNano = System.nanoTime();
        long startMillis = System.currentTimeMillis();
        WebRequestStat requestStat = new WebRequestStat(startNano, startMillis);
        WebRequestStat.set(requestStat);

        WebSessionStat sessionStat = getSessionStat(request);
        webAppStat.beforeInvoke();

        WebURIStat uriStat = webAppStat.getURIStat(request.path, false);

        if (isProfileEnabled) {
            Profiler.initLocal();
            Profiler.enter(request.path, Profiler.PROFILE_TYPE_WEB);
        }

        // 第一次访问时，uriStat这里为null，是为了防止404攻击。
        if (uriStat != null) {
            uriStat.beforeInvoke();
        }

        // 第一次访问时，sessionId为null，如果缺省sessionCreate=false，sessionStat就为null。
        if (sessionStat != null) {
            sessionStat.beforeInvoke();
        }


    }

    public void afterInvocation() {
        long endNano = System.nanoTime();
        WebRequestStat requestStat = WebRequestStat.current();
        requestStat.setEndNano(endNano);

        long nanos = endNano - requestStat.getStartNano();
        webAppStat.afterInvoke(null, nanos);

        Http.Request request = Http.Request.current();

        WebSessionStat sessionStat = getSessionStat(request);

        if (sessionStat != null) {
            sessionStat.afterInvoke(null, nanos);
            sessionStat.setPrincipal(getPrincipal(request));
        }

        WebURIStat uriStat = webAppStat.getURIStat(request.path, true);

        if (uriStat != null) {
            uriStat.afterInvoke(null, nanos);
        }

        WebRequestStat.set(null);

        if (isProfileEnabled) {
            Profiler.release(nanos);

            Map<ProfileEntryKey, ProfileEntryReqStat> requestStatsMap = Profiler.getStatsMap();
            if (uriStat != null) {
                uriStat.getProfiletat().record(requestStatsMap);
            }
            Profiler.removeLocal();
        }
    }


    public class WebStatFilterContextListener extends StatFilterContextListenerAdapter {

        @Override
        public void addUpdateCount(int updateCount) {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.addJdbcUpdateCount(updateCount);
            }
        }

        @Override
        public void addFetchRowCount(int fetchRowCount) {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.addJdbcFetchRowCount(fetchRowCount);
            }
        }

        @Override
        public void executeBefore(String sql, boolean inTransaction) {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.incrementJdbcExecuteCount();
            }
        }

        @Override
        public void executeAfter(String sql, long nanos, Throwable error) {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.addJdbcExecuteTimeNano(nanos);
                if (error != null) {
                    reqStat.incrementJdbcExecuteErrorCount();
                }
            }
        }

        @Override
        public void commit() {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.incrementJdbcCommitCount();
            }
        }

        @Override
        public void rollback() {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.incrementJdbcRollbackCount();
            }
        }

        @Override
        public void pool_connect() {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.incrementJdbcPoolConnectCount();
            }
        }

        @Override
        public void pool_close(long nanos) {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.incrementJdbcPoolCloseCount();
            }
        }

        @Override
        public void resultSet_open() {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.incrementJdbcResultSetOpenCount();
            }
        }

        @Override
        public void resultSet_close(long nanos) {
            WebRequestStat reqStat = WebRequestStat.current();
            if (reqStat != null) {
                reqStat.incrementJdbcResultSetCloseCount();
            }
        }
    }
}
